package com.vichat.common.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.vichat.common.constant.GlobalConstant;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
 * Created by ranjx on 2017/5/2.
 */
public class DesUtil {
    private static transient final Logger logger = LoggerFactory.getLogger(DesUtil.class);

    static int Asmversion = 0;
    static long maxuschar = 4294967295l;

    static int[] pc1 = {
            57, 49, 41, 33, 25, 17,  9,
            1, 58, 50, 42, 34, 26, 18,
            10,  2, 59, 51, 43, 35, 27,
            19, 11,  3, 60, 52, 44, 36,
            63, 55, 47, 39, 31, 23, 15,
            7, 62, 54, 46, 38, 30, 22,
            14,  6, 61, 53, 45, 37, 29,
            21, 13,  5, 28, 20, 12,  4
    };

    /* number left rotations of pc1 */
    static int[] totrot = {
            1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28
    };

    /* permuted choice key (table) */
    static int[] pc2 = {
            14, 17, 11, 24,  1,  5,
            3, 28, 15,  6, 21, 10,
            23, 19, 12,  4, 26,  8,
            16,  7, 27, 20, 13,  2,
            41, 52, 31, 37, 47, 55,
            30, 40, 51, 45, 33, 48,
            44, 49, 39, 56, 34, 53,
            46, 42, 50, 36, 29, 32
    };
    static long[][] kne_=null;
    static long[][] knd_=null;


    /* bit 0 is left-most in byte */
    static int[] bytebit = {
            0200,0100,040,020,010,04,02,01
    };

    static {
        DesUtil.init(GlobalConstant.DES_TOKEN);
    }

    public static void init(String deskey){
        if(knd_ == null)
            knd_ = deskey(deskey,1);
    }

    private static long[][]  deskey(String deskey, int decrypt)
    {
        char[] key = deskey.toCharArray();
        long [][] k = new long[16][2];
        int[] pc1m = new int[56];     /* place to modify pc1 into */
        int[] pcr = new int[56];      /* place to rotate pc1 into */
        int i,j,l,t;
        int m;
        char[] ks = new char[8];

        for (j=0; j<56; j++) {      /* convert pc1 to bits of key */
            l=pc1[j]-1;     /* integer bit location  */
            m = l & 07;     /* find bit      */
            /* find which key byte l is in */
            /* and which bit of that byte */
            t=l>>3;
            //System.out.println(key[t]);
            pc1m[j]=(key[t] & bytebit[m])>0 ? 1 : 0;    /* and store 1-bit result */
            //System.out.println(pc1m[j]);
            //System.out.println("//////////////////////");
        }
        for (i=0; i<16; i++) {      /* key chunk for each iteration */
            ks = new char[8];    /* Clear key schedule */
            for (j=0; j<56; j++)    /* rotate pc1 the right amount */
                pcr[j] = pc1m[(l=j+totrot[decrypt>0? 15-i : i])<(j<28? 28 : 56) ? l: l-28];
                /* rotate left and right halves independently */
            for (j=0; j<48; j++){   /* select bits individually */
                /* check bit that goes to ks[j] */
                if (pcr[pc2[j]-1] > 0){
                    /* mask it in if it's there */
                    l= j % 6;
                    ks[j/6] |= bytebit[l] >> 2;
                }
            }
            /* Now convert to packed odd/even interleaved form */
            k[i][0] = ((long)ks[0] << 24)
                    | ((long)ks[2] << 16)
                    | ((long)ks[4] << 8)
                    | ((long)ks[6]);
            k[i][1] = ((long)ks[1] << 24)
                    | ((long)ks[3] << 16)
                    | ((long)ks[5] << 8)
                    | ((long)ks[7]);
            if(Asmversion>0){
                /* The assembler versions pre-shift each subkey 2 bits
                 * so the Spbox indexes are already computed
                 */
                k[i][0] <<= 2;
                k[i][1] <<= 2;
            }
        }
        return k;
    }

    private static long F(long l,long r,long[] key){

        long[][] Spbox = new long[8][64];
        long work = ((r >> 4) | ((r << 28)& maxuschar)  ) ^ key[0];
        l ^= Spbox[6][(new Long(work & 0x3f)).intValue()];
        l ^= Spbox[4][(new Long((work >> 8) & 0x3f)).intValue()];
        l ^= Spbox[2][(new Long((work >> 16) & 0x3f)).intValue()];
        l ^= Spbox[0][(new Long((work >> 24) & 0x3f)).intValue()];
        work = r ^ key[1];
        l ^= Spbox[7][(new Long(work & 0x3f)).intValue()];
        l ^= Spbox[5][(new Long((work >> 8) & 0x3f)).intValue()];
        l ^= Spbox[3][(new Long((work >> 16) & 0x3f)).intValue()];
        l ^= Spbox[1][(new Long((work >> 24) & 0x3f)).intValue()];
        return work;

    }


    /* Encrypt or decrypt a block of data in ECB mode */
    private static byte[] des(long[][] ks, long[] block){

        /* Key schedule */
            /* Data block */

        long left,right,work;

        /* Read input block and place in left/right in big-endian order */
        left = (block[0] << 24)| (block[1] << 16)| (block[2] << 8)| block[3];
        right = (block[4] << 24) | (block[5] << 16)| (block[6] << 8)| block[7];

        /* Hoey's clever initial permutation algorithm, from Outerbridge
         * (see Schneier p 478)
         *
         * The convention here is the same as Outerbridge: rotate each
         * register left by 1 bit, i.e., so that "left" contains permuted
         * input bits 2, 3, 4, ... 1 and "right" contains 33, 34, 35, ... 32
         * (using origin-1 numbering as in the FIPS). This allows us to avoid
         * one of the two rotates that would otherwise be required in each of
         * the 16 rounds.
         */
        work = ((left >> 4) ^ right) & 0x0f0f0f0f;
        right ^= work;
        left ^= (work << 4) & maxuschar;
        work = ((left >> 16) ^ right) & 0xffff;
        right ^= work;
        left ^= (work << 16)& maxuschar;
        work = ((right >> 2) ^ left) & 0x33333333;
        left ^= work;
        right ^= (work << 2) & maxuschar;
        work = ((right >> 8) ^ left) & 0xff00ff;
        left ^= work;
        right ^= (work << 8) & maxuschar;
        right = ((right << 1) & maxuschar) | (right >> 31);
        work = (left ^ right) & 0xaaaaaaaa;
        left ^= work;
        right ^= work;
        left = ((left << 1) & maxuschar) | (left >> 31);

        /* Now do the 16 rounds */
        work = F(left,right,ks[0]);
        work = F(right,left,ks[1]);
        work = F(left,right,ks[2]);
        work = F(right,left,ks[3]);
        work = F(left,right,ks[4]);
        work = F(right,left,ks[5]);
        work = F(left,right,ks[6]);
        work = F(right,left,ks[7]);
        work = F(left,right,ks[8]);
        work = F(right,left,ks[9]);
        work = F(left,right,ks[10]);
        work = F(right,left,ks[11]);
        work = F(left,right,ks[12]);
        work = F(right,left,ks[13]);
        work = F(left,right,ks[14]);
        work = F(right,left,ks[15]);

        /* Inverse permutation, also from Hoey via Outerbridge and Schneier */
        right = ((right << 31) & maxuschar) | (right >> 1);
        work = (left ^ right) & 0xaaaaaaaa;
        left ^= work;
        right ^= work;
        left = (left >> 1) | ((left  << 31)&maxuschar);
        work = ((left >> 8) ^ right) & 0xff00ff;
        right ^= work;
        left ^= (work << 8) & maxuschar ;
        work = ((left >> 2) ^ right) & 0x33333333;
        right ^= work;
        left ^= (work << 2) & maxuschar;
        work = ((right >> 16) ^ left) & 0xffff;
        left ^= work;
        right ^= (work << 16) & maxuschar;
        work = ((right >> 4) ^ left) & 0x0f0f0f0f;
        left ^= work;
        right ^= (work << 4) & maxuschar;

        /* Put the block back into the user's buffer with final swap */
        byte[] res = new byte[8];

        res[0] = (new Long((right<<32) >> 56)).byteValue();
        res[1] = (new Long((right<<40) >> 56)).byteValue();
        res[2] = (new Long((right<<48) >> 56)).byteValue();
        res[3] = (new Long((right<<56) >> 56)).byteValue();
        res[4] = (new Long((left<<32) >> 56)).byteValue();
        res[5] = (new Long((left<<40) >> 56)).byteValue();
        res[6] = (new Long((left<<48) >> 56)).byteValue();
        res[7] = (new Long((left<<56) >> 56)).byteValue();
        return res;
    }

    public static String  decryptbyte(byte[] srcbyte){
        int alllen = srcbyte.length;
        int destime = alllen/8;
        int sublen = alllen % 8;
        if(sublen>0){
            destime++;
        }
        StringBuilder sb = new StringBuilder();
        for(int i=0; i<destime;i++){
            int startpos = i*8;
            int usesub = 0;
            long[] cryptlong = new long[8];
            for(int ti=0;ti<8;ti++){
                if(startpos<alllen){
                    cryptlong[ti] = srcbyte[startpos++]&0xff;
                }else{
                    usesub++;
                    cryptlong[ti]=0;
                }
            }

            byte[] resArray = des(knd_,cryptlong);


            String resstr = null;
            try {
                if(usesub>0){
                    byte[] tmpArray = new byte[8-usesub];
                    for(int tti=0; tti<tmpArray.length; tti++){
                        tmpArray[tti] = resArray[tti];
                    }
                    resstr = new String(tmpArray,"UTF-8");
                }else{
                    resstr = new String(resArray,"UTF-8");
                }
            } catch (Exception e) {

            }

            sb.append(resstr);

        }
        return sb.toString();
    }

    public static String decrypt(String codestr) throws IOException{
        return decrypt(codestr, true);
    }

    public static String decrypt(String codestr, boolean decodeBase64) throws IOException{
        if(decodeBase64){
            byte[] srcbyte = Base64.decodeBase64(codestr);
            return decryptbyte(srcbyte);
        }
        return decryptbyte(codestr.getBytes());
    }

    public static byte[] getBase64SsoKey(String codestr) throws IOException{
        byte[] srcbyte = Base64.decodeBase64(codestr);
        return srcbyte;
    }

    public static long getUid(String ssokey) throws IOException {
        long uid=0;
        try{
            String res = DesUtil.decrypt(ssokey);
            JSONObject obj = JSON.parseObject(res);
            uid = Long.parseLong(obj.get("uid").toString());
        }catch(Exception e){
            logger.error("获取用户uid异常");
        }
        return uid;
    }

    public static void main(String[]args) {
        try {
            //TqMH8F6-esnpuMr3Tij1unxyagMs8uO--U8FvMPWiwaxtRVpM7Ls3_5yd8biFMenWbswdl8AVHr9Tj4MLXFk0CQFXbSolvmgTyQABhlYRpoqY5aPo9lWQjQIlx74pn_l
            //txG6lpgRNREzMDAwMDAwMjARHBGdkp6aETUR2klG2nd12WlwERwRs5q0ETURMhEcEZmSk5oRNREyERwRmZKTmq-5mrERNRERHBGTkrERNRERHBG5mrERNRERHBG4mpKTlJqxETURERwRlJ-6s5oRNRERHBGTlriWtZqdETURERwRmJq5ETURMBEcEbizETURMjg2MzswNDg6OTYyOxEcEZSfnZ-xETWnrhwRlpianbiWmbYRNaeuHBGTurOasRE1p66-
            String ssokey = "txG6lpgRNREzMDAwMDAwMjARHBGdkp6aETUR2klG2nd12WlwERwRs5q0ETURMhEcEZmSk5oRNREyERwRmZKTmq-5mrERNRERHBGTkrERNRERHBG5mrERNRERHBG4mpKTlJqxETURERwRlJ-6s5oRNRERHBGTlriWtZqdETURERwRmJq5ETURMBEcEbizETURMjg2MzswNDg6OTYyOxEcEZSfnZ-xETWnrhwRlpianbiWmbYRNaeuHBGTurOasRE1p66-";
            System.out.print(getUid(ssokey));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
